基础 Web 漏洞攻击与防御的思考
0X00 前言
未知攻焉知防,攻与防的较量每天都在上演,但这些攻击都逃不过这几大基本的漏洞类型,下面我简单的列举的了几个漏洞类型,并对其中比较重要的漏洞的产生原因、攻击方法、防御措施做了一个简单的分析,并加入了一些自己的思考。
0X01 页面展示类漏洞
一、XSS
1.XSS 的类型
(1)反射型
攻击者会通过社会工程学手段,发送一个URL连接给用户打开,在用户打开页面的同时,浏览器会执行页面中嵌入的恶意脚本。
(2)存储型
攻击者利用web应用程序提供的录入或修改数据功能,将数据存储到服务器或用户cookie中,当其他用户浏览展示该数据的页面时,浏览器会执行页面中嵌入的恶意脚本。所有浏览者都会受到攻击。
(3)DOM型
由于html页面中,定义了一段JS,JS 代码会根据用户的输入,动态显示一段html代码,攻击者可以在输入时,插入一段恶意脚本,最终展示时,会执行恶意脚本。
DOM跨站和以上两个跨站攻击的差别是,DOM跨站是纯页面脚本的输出,只有规范使用JAVASCRIPT,才可以防御
2.XSS 的本质
本质就是执行 JS 脚本,所以 js 能做什么XSS 就能做什么,最主要的还是一种交互,向外界发送数据或者从外界获取数据,再或者就是直接完成一些动作,如跳转、模拟点击等。
(1)发送数据体现在向外发送cookie 或者 CSRF token 等,来帮助攻击者获取令牌伪造身份
(2)从外界获取数据体现在利用 XSS 进行钓鱼,比如在该页面上面引入外部页面,覆盖原有页面掩人耳目
(3)完成动作体现在,URL 重定向攻击,带受害者去另一个恶意页面,进行钓鱼以及模拟用户发送请求(CSRF),如自我复制的 XSS 蠕虫
3.XSS 的产生原因
对用户数据过滤不严而将恶意的 JS 代码直接输入了 html 标签,当页面加载时便会执行。
4.解决方案
1.在HTML/XML中显示“用户可控数据”前,应该进行html escape转义。
2.在javascript内容中输出的“用户可控数据”,需要做javascript escape转义。
3.对输出到富文本中的“用户可控数据”,做富文本安全过滤(允许用户输出HTML的情况)。
4.一些html标签的属性,需要,如果接收“用户可控数据”,需要做安全检查。
5.DOM XSS 请规范使用 javascript,遵守 javascript 安全编码规范
6.在给用户设置认证COOKIE时,加入HTTPONLY
7.在style内容中输出的“用户可控数据”,需要做CSS escape转义
8.ajax 中的 xml json 中的用户可控数据 做 escape 转义
二、Third-party script references
1.Third-party script references的原理及危害
Third-party script references,引用第三方网站脚本或iframe指向第三方网站。
当html页面引用了第三方网站的脚本,或者有iframe指向第三方网站时,一旦这个第三方网站出现安全问题,被黑客控制写入恶意脚本,那么该页面或脚本也会展示在自己网站上,会导致自己网站的安全性大大降低,访问者间接受到影响。
2.攻击示例
当自己网站上有这样一段代码的时候
<script src="http://www.another.com/some.js"></script>
这段代码会执行www.another.com网站下的some.js脚本。
或者自己网站上有着这样一段代码
下面是一段引用第三方网站iframe的代码:
<iframe src="http://www.another.com/some.html"></iframe>
这段代码会同时打开 http://www.another.com/some.html 这个页面。
恶意用户可以做以下攻击:恶意用户会先入侵 www.some.com 这个网站,之后将恶意代码加入 http://www.another.com/some.js ,当这个文件有恶意代码时,也会被展示在自己的web页面上。
3.解决方案
正确使用 CSP 来禁用外部脚本,或者用 CSP 设置脚本来源白名单
0X02 伪装欺骗类漏洞
一、CSRF
1.CSRF 的原理
Cross-Site Request Forgery(CSRF),跨站请求伪造攻击。
攻击者在用户浏览网页时,利用页面元素(例如img的src),强迫受害者的浏览器向Web应用程序发送一个改变用户信息的请求。由于发生CSRF攻击后,攻击者是强迫用户向服务器发送请求,所以会造成用户信息被迫修改,更严重者引发蠕虫攻击。
2.CSRF 的攻击方式及危害
CSRF攻击可以从站外和站内发起。
(1)从站内发起CSRF攻击:需要利用网站本身的业务,比如“自定义头像”功能,恶意用户指定自己的头像URL是一个修改用户信息的链接,当其他已登录用户浏览恶意用户头像时,会自动向这个链接发送修改信息请求。
(2)从站外发起CSRF攻击:则需要恶意用户在自己的服务器上,放一个自动提交修改个人信息的html页面,并把页面地址发给受害者用户,受害者用户打开时,会发起一个请求。如果恶意用户能够知道网站管理后台某项功能的URL,就可以直接攻击管理员,强迫管理员执行恶意用户定义的操作。
3.CSRF 的本质
本质就是利用 JS 脚本以及攻击者精心构造的恶意请求,结合用户的某网站 SESSION 已经存在的条件,在用户不知不觉中向该网站发起篡改请求。
再结合我们之前说的 XSS 的攻击方式的本质,我们也不难发现两者之间的紧密联系,这也是两者经常配合利用的原因,两者的结合也是钓鱼人经常利用的手法。
(1)网站内部脚本:因为当网站出现 XSS 漏洞的时候,就意味着能插入 JS 脚本,那就能在用户不知情的情况下向本站发起请求,这种请求是模仿来源于本站用户的请求的,如果用户是管理员且登录后台,攻击者在知道后台表单格式的情况下,就可以构造针对性的请求使之被莫名篡改,达成攻击目的。
(2)网站外部脚本:如果网站出现 XSS ,那么就可以引入一个外部页面,这个页面可以是任意页面,用户看起来就是到了另一个页面(注意此时地址栏的 URL 是不变的,当然用户不会注意到),我们可以在这个页面里面引入一个 iframe,iframe 引入我们的恶意请求(当然我们要设置 iframe 宽高为 0),这样也同样达到了隐形篡改的目的。
4.解决方案
(1)Token 防御
遵循以下三步可预防 CSRF:
1.在用户登陆时,设置一个CSRF的随机TOKEN,存放在用户的设置了 http-only 的cookie中,或者是 session 中,当用户浏览器关闭、或用户再次登录、或退出时,清除token。
2.在表单中,生成一个隐藏域,它的值就是COOKIE中随机TOKEN。
3.表单被提交后,就可以在接收用户请求的web应用中,判断表单中的TOKEN值是否和用户 http-only 的COOKIE中的或者是服务器端的 sesson 中的TOKEN值一致,如果不一致或没有这个值,就判断为CSRF攻击,同时记录攻击日志
由于攻击者无法预测每一个用户登录时生成的那个随机TOKEN值,所以无法伪造这个参数。
(2)同源策略
对于 ajax 形式的 csrf ,同源策略是一概拒绝的
(3)验证 referer :
(1)但是由于 referer 是浏览器设置的,属于第三方,这样就把安全寄托于第三方之上,某种程度来说也是不是很安全,虽然已经安全做的很好了
(2)由于用户可以自己设置不生成 referer ,因此在这种情况下不是很好用
(3) referer 只能用来防御外部脚本的 CSRF ,但是对于网站本身由 XSS 造成的 CSRF 是无法防御的。
注意:
1.放在 http-only 的 cookie 里面是为了,防止出现 正规网站的 xss 获取 cookie 中的 token 后在发送 请求
2.XSS 漏洞高于 SCRF 如果出现了 xss 的话,很有可能回绕过 CSRF 防护进行攻击
3.当出现GET请求修改用户数据时,一旦在url中出现了csrftoken,当前页面就不允许出现用户定义的站外链接,否则攻击者可以引诱用户点击攻击者定义的链接,访问在自己的网站,从referer中,获取url中的csrftoken,造成csrftoken泄露。
二、URL Redirect
1.URL Redirect 的原理
Web应用程序接收到用户提交的URL参数后,没有对参数做“可信任URL”的验证,就向用户浏览器返回跳转到该URL的指令。
2.URL Redirect攻击方式及危害
如果 xxxx.com 这个可信网站下的某个web应用程序存在这个漏洞,恶意攻击者可以发送给用户一个看上去是指向 xxxx.com网站的链接,但是用户打开后,实际上早就不在 xxxx.com 网站下了,而是跳转到了攻击者精心设计的钓鱼网站页面,而此时受害者还以为自己在 xxxx.com 网站下,这就导致用户被钓鱼攻击,账号被盗,或账号相关财产被盗。
例如这样一段代码
if(checklogin(request)){
response.sendRedirect(request.getParameter("url"));
}
这段代码并没有检验 url 的跳转地址就直接跳转了,导致了该漏洞的发生,同样,这也是钓鱼人最喜欢的攻击方式
3.攻击举例
攻击者某天盗了某管理员同事的QQ号,然后给这个管理员发了一个链接,链接如下:
http://admin.xxxx.com/member/sigin.htm?done=http://www.hacker.com:8080/admin/sigin.htm
因为这个链接很长,管理员可能就看了一下开头,感觉是自己管理界面的 URL ,然后以为有什么事,就点开了,但是没想到这里存在一个 URL Redirect 漏洞,实际访问的是 http://www.hacker.com:8080/admin/sigin.htm 这是一个攻击者精心构造的,和管理后台界面一样的页面,然后要求管理员输入密码,这时候其实输入任何密码都会说错误,然后就会跳转到真正的登录界面,此时管理员并没有发觉,再次输入密码,成功进入后台,但此时攻击者早就掌握了管理员的账号密码了。
4.解决方案
1.设置并验证 token
为了保证用户所点击的URL,是从web应用程序中生成的URL,所以要做TOKEN验证。
(1)当用户访问需要生成跳转URL的页面时,首先生成随机token,并放入cookie。
(2)在显示连接的页面上生成URL,在URL参数中加入token。
示例:
http://admin.xxxx.com/member/sigin.htm?done=http://www.xxxx.com&token=5743892783432432
(3)应用程序在跳转前,判断token是否和cookie中的token一致,如果不一致,就判定为URL跳转攻击,并记录日志.
(4)如果在javascript中做页面跳转,需要判断域名白名单后,才能跳转。
2.设置地址白名单
(1)如果应用只有跳转到本集团/公司网站的需求,可以设置白名单,判断目的地址是否在白名单列表中,如果不在列表中,就判定为URL跳转攻击,并记录日志
(2)不允许配置集团以外网站到白名单列表中。
这两个方案都可以保证所有在应用中发出的重定向地址,都是可信任的地址。
三、SSRF
1.SSRF的原理及危害
很多web应用都提供了从其他的服务器上获取数据的功能。使用用户指定的URL,web应用可以获取图片,下载文件,读取文件内容等。这个功能如果被恶意使用,可以利用存在缺陷的web应用作为代理攻击远程和本地的服务器。这种形式的攻击称为服务端请求伪造攻击(Server-side Request Forgery)。
一般情况下, SSRF攻击的目标是从外网无法访问的内部系统(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统 )
SSRF 形成的原因大都是由于 服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制 比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。
攻击者利用ssrf可以实现的攻击主要有5种:
1.可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息;
2.攻击运行在内网或本地的应用程序(比如溢出);
3.对内网web应用进行指纹识别,通过访问默认文件实现;
4.攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli等);
5.利用file协议读取本地文件等。
6.还可以使用 gopher 协议拓宽我们的攻击面,比如实现攻击内网的 mysql
2.攻击方式
3.解决方案
解决方案无非就两种思路,一种是打断从内网出外网的道路,另一种就是打断边界服务器进内网的道路,当然实际中这两者需要结合使用
打断由内到外:
1.过滤内网服务器对公网服务器请求的响应。如果Web应用是获取某一类型的文件,在把返回结果展示给用户之前应先验证返回的信息是否符合文件类型标准,比如返回信息应为图片,如果返回信息是HTML,则停止将返回信息返回客户端。
2.统一错误提示信息,避免用户可以根据错误信息来判断远端服务器的端口状态。
打断由外到内:
1.在内网服务器的防火墙上限制公网服务器的请求端口为HTTP等协议常用端口,如:80、443、8080、8090。
2.若公网服务器的内网IP与内网无业务通信,建议将公网服务器对应的内网IP列入黑名单,避免应用被用来获取内网数据。
3.内网服务器禁用不必要的协议,仅允许HTTP和HTTPS请求,防止类似于file:///、gopher://、ftp:// 等协议引起的安全问题。
0X03 注入类漏洞
一、SQLi
1.SQLi 的原理
当应用程序将用户输入的内容,拼接到SQL语句中,一起提交给数据库执行时,就会产生SQL注入威胁。
2.SQLi 的攻击方式以及危害
由于用户的输入,也是SQL语句的一部分,所以攻击者可以利用这部分可以控制的内容,注入自己定义的语句,改变SQL语句执行逻辑,让数据库执行任意自己需要的指令。通过控制部分SQL语句,攻击者可以查询数据库中任何自己需要的数据,利用数据库的一些特性,可以直接获取数据库服务器的系统权限。
3.解决方案
使用预处理的方式去执行SQL语句,对所有传入SQL语句中的变量,做绑定。这样,用户拼接进来的变量,无论内容是什么,都会被当做替代符号“?”所替代的值,数据库也不会把恶意用户拼接进来的数据,当做部分SQL语句去解析,从而保证了数据库的安全性。
Java odbc示例:
com.mysql.jdbc.Connection conn = db.JdbcConnection.getConn();
final String sql = "select * from product where pname like ?";
java.sql.PreparedStatement ps = (java.sql.PreparedStatement) conn.prepareStatement(sql);
ps.setObject(1, "%"+request.getParameter("pname")+"%");
ResultSet rs = ps.executeQuery();
PHP示例:
$query = "INSERT INTO myCity (Name, CountryCode, District) VALUES (?,?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("sss", $val1, $val2, $val3);
$val1 = 'Stuttgart';
$val2 = 'DEU';
$val3 = 'Baden-Wuerttemberg';
/* Execute the statement */
$stmt->execute();
当然和对于 PHP 而言现在有一种方法叫做 PDO,和 mysqli 原理类似,但是支持更多的数据库类型,不像 mysqli 只支持 MySQL 一种,当然 mysqli 这种针对性的扩展相对于 MySQL 数据库而言效果更好。两者更详细的比较可以参考这篇文章
4.常见问题
用了 PDO 这种预编译就能完全预防 SQL 注入了吗?
(1) PDO 并不能满足全部的 SQL 语句,有些语句过于复杂,使用 PDO 就非常困难,
(2)我们说安全最薄弱环节的还是人,有一些老的系统是从原来的语句改成使用预编译的,这种情况下就很可能有漏网之鱼
(3)p 牛写过一篇文章,关于 TP5 的一处注入,在 PDO::ATTR_EMULATE_PREPARES => false(模拟预编译为 flase)的情况下如果能控制PDO 执行三部曲
prepare($SQL) 编译SQL语句
bindValue($param, $value) 将value绑定到param的位置上
execute() 执行
中的第一步,就能让其在服务器端编译的时候报错,而不像控制第二部报错了不能执行到第三部
(4)PHP 5.3.6及以前版本的PDO的bindParam,bindValue潜在的安全隐患,PHP 5.3.6及老版本,并不支持在DSN中定义charset属性(会忽略之),这时如果使用PDO的本地转义,仍然可能导致SQL注入,这个点的来源是这篇文章
二、Code injection
1.Code injection 原理及危害
在 web 应用运行的过程中,用户输入的参数直接或者间接地作为代码的一部分传入到可以执行代码的函数 如:php 中的 eval()当中,在没有过滤或者过滤的不完全的情况下,攻击者可以控制这个输入实现任意代码在服务器端执行,从而实现各种攻击目的。
恶意用户可以写一个远程控制木马,直接获取服务器控制权限,所有服务器上的资源都会被恶意用户获取和修改,甚至可以直接控制数据库。
2.攻击示例
servlet 代码:
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
try {
File file = File.createTempFile("JavaRuntime", ".java", new File(System.getProperty("user.dir")));
String filename = file.getName();
String classname = filename.substring(0, filename.length() - 5);
String[] args = new String[] {
"-d",
System.getProperty("user.dir"),
filename
};
PrintWriter outfile = new PrintWriter(new FileOutputStream(file));
outfile.write("public class " + classname + "{public void myfun(String args)"
+ "{try {"+ request.getParameter("code")+ "} catch (Exception e) {}}}");
outfile.flush();
outfile.close();
(new Main()).compile(args, outfile);
URL url = new URL("file://"+ file.getPath().substring(0,file.getPath().lastIndexOf("\\") + 1));
java.net.URLClassLoader myloader = new URLClassLoader(new URL[] { url }, Thread.currentThread().getContextClassLoader());
Class cls = myloader.loadClass(classname);
cls.getMethod("myfun", new Class[] { String.class }).invoke(cls.newInstance(), new Object[] { "" });
} catch (Exception se) {
se.printStackTrace();
}
out.println();
out.flush();
out.close();
}
这个代码中 request.getParameter(“code”) 中的 code 用户可控,且没有有效的过滤,我们就可以构造下面的语句进行攻击
Payload:
http://www.xxxx.com/servlet/Active?code=System.out.println("kxlzx");
3.解决方案
对于应用代码中的可以执行代码的函数的输入全部来源于开发人员的编写,来确保用户不可控,对于一定要用户确定代码的情况,用户能做的只是根据开发人员提供的接口选择对应的代码执行。
三、XML injection(普通)
1.XML injection(普通) 原理及危害
XML是存储数据的地方,如果在查询或修改时,如果没有做转义,直接输入或输出数据,都将导致XML注入漏洞。攻击者可以修改XML数据格式,增加新的XML节点,对数据处理流程产生影响。
2.攻击示例
这里是一个保存注册用户信息为xml的例子:
final String GUESTROLE = "guest_role";
...
//userdata是准备保存的xml数据,接收了name和email两个用户提交来的数据。
String userdata = "<USER role="+ GUESTROLE+ "><name>"+ request.getParameter("name")+ "</name><email>"+ request.getParameter("email")+"</email></USER>";
//保存xml
userDao.save(userdata);
对用户的输入没有做任何的过滤,原本注册用户后,应该产生一条这样的用户记录:
<?xml version="1.0" encoding="UTF-8"?>
<USER role="guest_role">
<name>user1</name>
<email>user1@a.com</email>
</USER>
但是恶意用户的输入却是下面这个样子
user1@a.com</email></USER><USER role="admin_role"><name>kxlzx</name><email>user2@a.com
于是最后注册以后就变成了下面这个样子
<?xml version="1.0" encoding="UTF-8"?>
<USER role="guest_role">
<name>user1</name>
<email>user1@a.com</email>
</USER>
<USER role="admin_role">
<name>kxlzx</name>
<email>user2@a.com</email>
</USER>
这样就无形中增加了一个攻击者自定义的管理员
3.解决方案
在XML保存和展示前,对数据部分单独做xml escape。
String userdata = "<USER role="+GUESTROLE+"><name>"+
StringUtil.xmlencode(request.getParameter("name"))+"</name><email>"+
StringUtil.xmlencode(rrequest.getParameter("email"))+"</email></USER>";
字符会按照下面的映射关系进行转义
& --> &
< --> <
> --> >
" --> "
' --> '
四、XML injection(外部实体XXE)
1.XXE 的原理以及危害
XXE 全称 XML 外部实体注入,XML 可以使用 DTD 来规定整个 XML 的格式,DTD 中可以定义 XML 中的实体,且 DTD 可以外部引用,由于该实体会在后面的 XML 中调用,所以如果我们能控制该外部实体
的引用路径的话,我们就能向该路径发起请求,可以利用这个特性实现文件读取、内网探测、sqli、文件上传等操作,具体的分析我在我之前的一篇文章中介绍过了。
2.攻击示例
xml.php
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
echo $creds;
?>
payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
<creds>&goodies;</creds>
运行效果:
![此处输入图片的描述][7]
3.解决方案
方案一:使用语言中推荐的禁用外部实体的方法
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
.setFeature("http://xml.org/sax/features/external-general-entities",false)
.setFeature("http://xml.org/sax/features/external-parameter-entities",false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
方案二:手动黑名单过滤(不推荐)
过滤关键词:
<!DOCTYPE、<!ENTITY SYSTEM、PUBLIC
五、System command injection
1.System command injection 的原理及危害
系统命令执行攻击,是指代码中有一段执行系统命令的代码,而用户的输入可以直接或者间接地作为这个命令或者是命令的一部分,所以在没有对用户的输入进行过滤或者过滤不完全的时候,恶意攻击者可以通过这个功能直接执行系统命令从而直接控制目标服务器。
2.攻击示例
java:
Runtime.getRuntime().exec(request.getParameter("cmd"));
攻击者输入:
http://www.xxxx.com/servlet/command?cmd=shutdown
就能实现关机的功能
3.解决方案
所有需要执行的系统命令,必须是开发人员定义好的,不允许接收用户传来的参数,加入到系统命令中去。
0X04 文件操作类漏洞
一、File upload
1.File upload 原理及危害
Web应用程序在处理用户上传的文件时,没有判断文件的扩展名是否在允许的范围内,或者是判断过滤不到位,再或者就是存在某种可以被利用的解析漏洞,使文件被保存在服务器上,导致恶意用户可以上传可以被服务器解析的文件,甚至上传脚本木马到web服务器上,从而可以直接控制web服务器。如果配合文件包含漏洞的话能达到更好的效果。
2.攻击示例
下面的代码没有过滤扩展名:
java
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(
request.getRealPath("/")+getFIlename(request))));
ServletInputStream in = request.getInputStream();
int i = in.read();
while (i != -1) {
pw.print((char) i);
i = in.read();
}
pw.close();
php:
file_put_contents($_REQUEST["filename"],$_REQUEST["context"]);
假如第一段代码是xxxx.com下的一个web应用程序。这段代码将直接把用户上传的文件,保存在web目录中,上传后,用户可以通过
http://www.xxxx.com/jspmuma.jsp
访问上传的JSP木马,控制服务器
注:即使jsp文件不会被web容器解析执行,攻击者也可以上传自定义的htm文件,造成XSS攻击。
3.解决方案
1.检查上传文件扩展名白名单,不属于白名单内,不允许上传。
2.上传文件的目录必须是http请求无法直接访问到的。如果需要访问的,必须上传到其他(和web服务器不同的)域名下,并设置该目录为不解析jsp等脚本语言的目录。
3.上传文件要保存的文件名和目录名由系统随机生成,不可预测,更不允许用户自定义。
4.图片上传,要通过处理(缩略图、水印等),无异常后才能保存到服务器。
5.上传文件需要做日志记录
二、File download and Directory traversal
1.File download and Directory traversal 原理及危害
File download and Directory traversal,任意文件下载攻击和目录遍历攻击。
(1)处理用户请求下载文件时,允许用户提交任意文件路径,并把服务器上对应的文件直接发送给用户,这将造成任意文件下载威胁。
(2)如果让用户提交文件目录地址,就把目录下的文件列表发给用户,会造成目录遍历安全威胁。
恶意用户会变换目录或文件地址,下载服务器上的敏感文件、数据库链接配置文件、网站源代码等。
2.攻击示例
java:
String path = request.getParameter("path");
java.io.OutputStream os = response.getOutputStream();
java.io.FileInputStream fis = new java.io.FileInputStream(path);
byte[] b = new byte[1024];
int i = 0;
while ((i = fis.read(b)) > 0 ){
os.write(b, 0, i);
}
fis.close();
os.flush();
os.close();
php:
$o = file_get_contents($filename);
echo $o;
以上代码根据用户提交的path,从服务器上获取指定文件,展示给用户。而攻击者会变化参数中的文件名,下载服务器中的敏感文件,数据库配置文件等。
http://www.xxxx.com/filedownload.do?filename=/etc/passwd
3.解决方案
对文件操作功能,做到以下几点:
1.文件路径保存至数据库,让用户提交文件对应ID下载文件。
2.下载文件之前做权限判断。
3.文件放在web无法直接访问的目录下。
4.记录文件下载日志。
7.不允许提供目录遍历服务。
0X05 访问控制类漏洞
一、Vertical Access Control
1.Vertical Access Control 的原理及危害
由于web应用程序没有做权限控制,或仅仅在菜单上做了权限控制,导致的恶意用户只要猜测其他管理页面的URL,就可以访问或控制其他角色拥有的数据或页面,达到权限提升目的。
这个威胁可能导致普通用户变成管理员权限。
2.攻击示例
一个仅仅做了菜单控制的代码:
<tr><td><a href="/user.jsp">管理个人信息</a></td></tr>
<%if (power.indexOf("administrators")>-1){%>
管理所有用户
<%}%> < code>%}%>>
恶意用户,可以直接猜测“管理所有用户”的页面,通过URL访问,看到管理员页面,以管理员身份进行非法操作
3.解决方案
在打开管理页面URL时,首先判断当前用户是否拥有该页面的权限,如果没有权限,就判定为“权限提升”攻击,同时记录安全日志,同时建议使用成熟的权限框架处理权限问题,比如spring security。
二、Horizontal Access Control
1.Horizontal Access Control 原理与危害
Horizontal Access Control,访问控制攻击,也就是水平权限安全攻击。
Web应用程序接收到用户请求,修改某条数据时,没有判断数据的所属人,或判断数据所属人时,从用户提交的request参数(用户可控数据)中,获取了数据所属人id,导致恶意攻击者可以通过变换数据ID,或变换所属人id,修改不属于自己的数据。恶意用户可以删除或修改其他人数据。
2.攻击示例
访问数据层(dao),所有的更新语句操作,都可能产生这个漏洞。
以下代码存在这个漏洞,web应用在修改用户个人信息时,从从用户提交的request参数(用户可控数据)中,获取了userid,执行修改操作。
修改用户个人信息页面
<form action="/struts1/edituser.htm" method="post">
<input name="userid" type="hidden" value="<%=userid%>">
<table border="1">
<tr>
<td>username:</td>
<td><%=rs.getString("name")%></td>
</tr>
<tr>
<td>passwd:</td>
<td> <input name="pass" value="<%=rs.getString("pass")%>"></td>
</tr>
<tr>
<td>type:</td>
<td><%=rs.getString("type")%></td>
</tr>
<tr>
<td>realname:</td>
<td><input name="realname" value="<%=rs.getString("realname")%>"></td>
</tr>
<tr>
<td>email:</td>
<td> <input name="email" value="<%=rs.getString("email")%>"></td>
</tr>
<tr>
<td>tel:</td>
<td> <input name="tel" value="<%=rs.getString("tel")%>"></td>
</tr>
</table>
<html:submit/>
</form>
表单中,将用户的useird作为隐藏字段,提交给处理修改个人信息的应用。
下面代码是修改个人信息的应用
int userid=Integer.valueOf( request.getParameter("userid"));
String email=request.getParameter("email");
String tel=request.getParameter("tel");
String realname=request.getParameter("realname");
String pass=request.getParameter("pass");
JdbcConnection conn = null;
try {
conn = new JdbcConnection();
Object[] params = new Object[5];
params[0] = email;
params[1] = tel;
params[2] = realname;
params[3] = pass;
params[4] = userid;
final String sql = "update user set email=?,tel=?,realname=?,pass=? where userid=?";
conn.execUpdate(sql,params);
conn.closeConn();
这段代码是从request的参数列表中,获取userid,也就是表单提交上来的userid,之后修改userid对应的用户数据。而表单中的userid是可以让用户随意修改的,如果恶意用户提交时篡改了这个值,就能欺骗服务器成为另一个用户,从而修改其他用户的信息
3.解决方案
从用户的加密认证cookie中,获取当前用户的id,并且需要在执行的SQL语句中,加入当前用户id作为条件语句。由于是web应用控制的加密算法,所以恶意用户无法修改加密信息。
示例代码:
int userid=Integer.valueOf( GetUseridFromCookie(request));
String email=request.getParameter("email");
String tel=request.getParameter("tel");
String realname=request.getParameter("realname");
String pass=request.getParameter("pass");
JdbcConnection conn = null;
try {
conn = new JdbcConnection();
Object[] params = new Object[5];
params[0] = email;
params[1] = tel;
params[2] = realname;
params[3] = pass;
params[4] = userid;
final String sql = "update user set email=?,tel=?,realname=?,pass=? where userid=?";
conn.execUpdate(sql,params);
conn.closeConn();
代码中通过GetUseridFromCookie,从加密的COOKIE中获取了当前用户的id,并加入到SQL语句中的WHERE条件中
0X06 Session 管理
一、Cookie httponly flag
1.Cookie httponly flag的原理及危害
Cookie http only,是设置COOKIE时,可以设置的一个属性,如果COOKIE没有设置这个属性,该COOKIE值可以被页面 js 脚本读取
当攻击者发现一个XSS漏洞时,通常会写一段页面脚本,窃取用户的COOKIE,如果不设置这个属性就会出现因为XSS漏洞导致大面积用户COOKIE被盗,从而严重地危害业务安全。
2.攻击示例
设置cookie的代码
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie"));
这段代码没有设置http only属性,攻击者可以通过 JS 脚本获取用户的 cookie,详情见 XSS 漏洞
3.解决方案
设置cookie时,加入属性即可
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly");
4.常见问题
httponly已经可以防止用户cookie被窃取,还需要做XSS防御吗?
这个flag只能增加攻击者的难度,不能达到完全防御XSS攻击,并且 XSS 能做的事非常的多,不只是获取用户的 cookie
二、Cookie Secure flag
1.Cookie Secure flag 的概念及优点
Cookie Secure,是设置COOKIE时,可以设置的一个属性,设置了这个属性后,只有在https访问时,浏览器才会发送该COOKIE给服务器。
浏览器默认只要使用http请求一个站点,就会发送明文cookie,如果网络中有监控,可能被截获。
如果web应用网站全站是https的,可以设置cookie加上Secure属性,这样浏览器就只会在https访问时,发送cookie,这样攻击者即使窃听网络,也无法获取用户明文cookie
2.攻击示例
设置cookie的代码
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly");
这段代码没有设置Secure属性,如果此时进行网络监听,就可以看到没有设置Secure属性的COOKIE发送的数据包。
3.解决方案
在设置认证COOKIE时,加入Secure 属性。
代码:
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly ; Secure ");
此时如果再次访问http网站,抓数据包可以看到,已经不再发送这个COOKIE了。
三、Session Expires
1.Session Expires 的原理及危害
Session Expires,Session有效期安全攻击(会话固定漏洞)。
由于Session没有在web应用中设置强制超时时间,攻击者一旦曾经获取过用户的Session,就可以一直使用,从而可以伪造用户的身份做任何事。
2.攻击示例
设置cookie的代码
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly ; Secure ");
这段代码没有在服务器中设置强制超时时间。利用网络监听或者xss等一些手段获取用户的cookie,之后就可以一直使用用户身份登录。
3.解决方案
在设置认证cookie中,加入两个时间,一个是“即使一直在活动,也要失效”的时间,一个是“长时间不活动的失效时间”。并在web应用中,首先判断两个时间是否已超时,再执行其他操作。
示例:
// 判断会员的cookie是否过期
if (isLogin) {
String timeStampStr = (String) map.get(UserAuthenticationContext.TIMESTAMP);
long loginTime = 0;
try {
loginTime = Long.parseLong(timeStampStr);
} catch (NumberFormatException e) {
if (logger.isInfoEnabled()) {
logger.info(" loginId: " + usr.getLoginId() + " timestamp has exception " + timeStampStr);
}
}
long now = System.currentTimeMillis() / 1000;
if (now - loginTime > UserAuthenticationContext.COOKIE_VALIDITY) {
usr.setAuthenticated(false, true);
if (logger.isInfoEnabled()) {
logger.info("loginId: " + usr.getLoginId() + " loginTime: " + loginTime + " nowTime: " + now);
}
}
}
0X07 总结
实际上漏洞类型应该有更多,但是这里我仅仅是列出了比较常见的或者说是比较 “大” 的漏洞,对于一些更细小的漏洞没有阐述,这或许等过一段时间再回来补充。
[786%E8%A7%A3%20XXE%20%E6%BC%8F%E6%B4%9E/
6: https://picture-1253331270.cos.ap-beijing.myqcloud.com/XXE%E5%9B%9E%E6%98%BE%E8%AF%BB%E6%96%87%E4%BB%B6.png